﻿using Models;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Utils;

namespace AsyncAwaitDemo
{
    public partial class Form1 : Form
    {
        private DateTime _dt1;
        private System.Timers.Timer _timer;
        private string _testFileDownloadUrl = "http://desk-fd.zol-img.com.cn/t_s1920x1080c5/g6/M00/06/00/ChMkKWHXqV-IJFpUAEJatCdq_LUAAXW8AA1PvwAQlrM029.jpg?downfile=1642227460763.jpg"; //文件下载测试URL
        //private string _testFileDownloadUrl = "https://down21.xiazaidb.com/app/xiaomanghe.apk"; //文件下载测试URL
        //private string _testFileDownloadUrl = "https://codeload.github.com/0611163/DBHelper/zip/refs/heads/master"; //文件下载测试URL
        //private string _testFileDownloadUrl = "http://down-ww5.537a.com/soft/3/ce/com.yhong.muchun_51982ada.apk"; //文件下载测试URL
        //private string _testFileDownloadUrl = "https://dl.360safe.com/netunion/20140425/360se+191727+n3f07b78190.exe"; //文件下载测试URL 大一点的文件


        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //定时器会使用线程池中的一个线程
            _timer = new System.Timers.Timer();
            _timer.Interval = 100;
            _timer.Elapsed += _timer_Elapsed;
            _timer.Start();
        }

        #region Log
        private void Log(string log)
        {
            if (!this.IsDisposed)
            {
                string msg = DateTime.Now.ToString("mm:ss.fff") + " " + log + "\r\n\r\n";

                if (this.InvokeRequired)
                {
                    this.BeginInvoke(new Action(() =>
                    {
                        textBox1.AppendText(msg);
                    }));
                }
                else
                {
                    textBox1.AppendText(msg);
                }
            }
        }
        #endregion

        #region _timer_Elapsed
        private void _timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            int workerThreads;
            int completionPortThreads;
            int maxWorkerThreads;
            int maxCompletionPortThreads;
            ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);
            ThreadPool.GetMaxThreads(out maxWorkerThreads, out maxCompletionPortThreads);
            this.BeginInvoke(new Action(() =>
            {
                label1.Text = "程序当前使用线程数：" + (maxWorkerThreads - workerThreads) + "  workerThreads：" + workerThreads.ToString() + "  completionPortThreads：" + completionPortThreads;
            }));
        }
        #endregion

        #region button1_Click 测试同步方法
        private void button1_Click(object sender, EventArgs e)
        {
            Task.Run(() => //下载文件是耗时操作，需要在线程中执行，否则界面卡住
            {
                Log("开始");
                DateTime dt = DateTime.Now;

                //输入参数
                string arg1 = "1";
                string arg2 = "2";
                string arg3 = "3";

                //方法调用
                string result1 = SyncFunction(arg1);
                string result2 = SyncFunction(arg2);
                string result3 = SyncFunction(arg3);

                //输出结果
                Log(result1);
                Log(result2);
                Log(result3);

                Log("结束，耗时：" + DateTime.Now.Subtract(dt).TotalSeconds.ToString("0.000"));
            });
        }
        #endregion

        #region button2_Click 测试异步方法(三个异步方法并行，按顺序输出)
        private async void button2_Click(object sender, EventArgs e)
        {
            Log("开始");
            DateTime dt = DateTime.Now;

            //输入参数
            string arg1 = "1";
            string arg2 = "2";
            string arg3 = "3";

            //方法调用
            var t1 = AsyncFunction(arg1);
            var t2 = AsyncFunction(arg2);
            var t3 = AsyncFunction(arg3);
            string result1 = await t1;
            string result2 = await t2;
            string result3 = await t3;

            //输出结果
            Log(result1);
            Log(result2);
            Log(result3);

            Log("结束，耗时：" + DateTime.Now.Subtract(dt).TotalSeconds.ToString("0.000"));
        }
        #endregion

        #region button3_Click 测试异步方法(三个异步方法顺序执行，按顺序输出)
        private async void button3_Click(object sender, EventArgs e)
        {
            Log("开始");
            DateTime dt = DateTime.Now;

            //输入参数
            string arg1 = "1";
            string arg2 = "2";
            string arg3 = "3";

            //方法调用
            string result1 = await AsyncFunction(arg1);
            string result2 = await AsyncFunction(arg2);
            string result3 = await AsyncFunction(arg3);

            //输出结果
            Log(result1);
            Log(result2);
            Log(result3);

            Log("结束，耗时：" + DateTime.Now.Subtract(dt).TotalSeconds.ToString("0.000"));
        }
        #endregion

        #region button4_Click 测试状态机(三个异步方法顺序执行，按顺序输出)(等价于button3_Click)
        private void button4_Click(object sender, EventArgs e)
        {
            List<object> resultList = new List<object>();

            //回调、状态机，不使用async await语法糖
            Action<int, object> awaitAction = null;
            awaitAction = (state, result) =>
            {
                //输入参数
                string arg1 = "1";
                string arg2 = "2";
                string arg3 = "3";

                //方法调用
                if (state == 0)
                {
                    Log("开始");
                    _dt1 = DateTime.Now;

                    AsyncFunctionWithCallback(arg1, r => awaitAction(1, r));
                    return;
                }
                if (state == 1)
                {
                    resultList.Add(result);
                    AsyncFunctionWithCallback(arg2, r => awaitAction(2, r));
                    return;
                }
                if (state == 2)
                {
                    resultList.Add(result);
                    AsyncFunctionWithCallback(arg3, r => awaitAction(3, r));
                    return;
                }

                //输出结果
                if (state == 3)
                {
                    resultList.Add(result);
                    foreach (object item in resultList)
                    {
                        Log(item != null ? item.ToString() : "null");
                    }
                    Log("结束，耗时：" + DateTime.Now.Subtract(_dt1).TotalSeconds.ToString("0.000"));
                    return;
                }
            };
            awaitAction(0, null);
        }
        #endregion

        #region button5_Click 测试状态机(三个异步方法并行，按顺序输出)(等价于button2_Click)
        private void button5_Click(object sender, EventArgs e)
        {
            object[] resultArray = new object[3];

            //是否已调用回调函数
            bool _completedCalled = false;

            //锁
            object _lock = new object();

            //输出结果
            Action<object[]> output = arr =>
            {
                if (arr.Count(a => a != null) == 3)
                {
                    lock (_lock)
                    {
                        if (!_completedCalled)
                        {
                            _completedCalled = true;
                            foreach (object item in arr)
                            {
                                Log(item != null ? item.ToString() : "null");
                            }
                            Log("结束，耗时：" + DateTime.Now.Subtract(_dt1).TotalSeconds.ToString("0.000"));
                        }
                    }
                }
            };

            //回调、状态机，不使用async await语法糖
            Action<int, object> awaitAction = null;
            awaitAction = (state, result) =>
            {
                //输入参数
                string arg1 = "1";
                string arg2 = "2";
                string arg3 = "3";

                //方法调用
                if (state == 0)
                {
                    Log("开始");
                    _dt1 = DateTime.Now;

                    AsyncFunctionWithCallback(arg1, r => awaitAction(1, r));
                    AsyncFunctionWithCallback(arg2, r => awaitAction(2, r));
                    AsyncFunctionWithCallback(arg3, r => awaitAction(3, r));
                    return;
                }

                //输出结果
                if (state == 1)
                {
                    resultArray[0] = result;
                    output(resultArray);
                    return;
                }
                if (state == 2)
                {
                    resultArray[1] = result;
                    output(resultArray);
                    return;
                }
                if (state == 3)
                {
                    resultArray[2] = result;
                    output(resultArray);
                    return;
                }
            };
            awaitAction(0, null);
        }
        #endregion

        #region button6_Click 测试状态机(三个异步方法并行，按顺序输出)(等价于button2_Click)(相对于button5_Click更优雅的实现)
        private void button6_Click(object sender, EventArgs e)
        {
            Log("开始");
            DateTime dt = DateTime.Now;

            //输入参数
            string arg1 = "1";
            string arg2 = "2";
            string arg3 = "3";

            //操作结果
            object result1 = null;
            object result2 = null;
            object result3 = null;

            //方法调用
            Await await = new Await();
            await.Add(() => AsyncFunctionWithCallback(arg1, r => await.Collect(r, out result1)))
                .Add(() => AsyncFunctionWithCallback(arg2, r => await.Collect(r, out result2)))
                .Add(() => AsyncFunctionWithCallback(arg3, r => await.Collect(r, out result3)))
                .Completed(resultList => //输出结果
                {
                    Log(result1 != null ? result1.ToString() : "null");
                    Log(result2 != null ? result2.ToString() : "null");
                    Log(result3 != null ? result3.ToString() : "null");
                    Log("结束，耗时：" + DateTime.Now.Subtract(dt).TotalSeconds.ToString("0.000"));
                })
                .Start();
        }
        #endregion

        #region button7_Click 不使用状态机，执行带回调的异步方法(基本等价于button1_Click)
        private void button7_Click(object sender, EventArgs e)
        {
            Task.Run(() =>
            {
                Log("开始");
                DateTime dt = DateTime.Now;

                //输入参数
                string arg1 = "1";
                string arg2 = "2";
                string arg3 = "3";

                //操作结果
                object result1 = null;
                object result2 = null;
                object result3 = null;

                //方法调用
                AsyncFunctionWithCallback(arg1, r => result1 = r);
                AsyncFunctionWithCallback(arg2, r => result2 = r);
                AsyncFunctionWithCallback(arg3, r => result3 = r);

                //等待三个操作结果全部返回，三个操作结果返回之前，始终占用一个线程
                while (result1 == null || result2 == null || result3 == null)
                {
                    Thread.Sleep(1);
                }

                //输出结果
                Log(result1 != null ? result1.ToString() : "null");
                Log(result2 != null ? result2.ToString() : "null");
                Log(result3 != null ? result3.ToString() : "null");
                Log("结束，耗时：" + DateTime.Now.Subtract(dt).TotalSeconds.ToString("0.000"));
            });
        }
        #endregion

        #region 同步方法
        /// <summary>
        /// 同步方法
        /// </summary>
        private string SyncFunction(string arg)
        {
            MemoryStream ms = HttpUtil.HttpDownloadFile(_testFileDownloadUrl);
            return "同步方法 arg=" + arg + " 下载文件的字节数组长度=" + ms.Length;
        }
        #endregion

        #region 异步方法
        /// <summary>
        /// 异步方法
        /// </summary>
        private async Task<string> AsyncFunction(string arg)
        {
            MemoryStream ms = await HttpUtil.HttpDownloadFileAsync(_testFileDownloadUrl);
            return "异步方法 arg=" + arg + " 下载文件的字节数组长度=" + ms.Length;
        }
        #endregion

        #region 带回调的异步方法
        /// <summary>
        /// 带回调的异步方法
        /// </summary>
        /// <param name="arg">传入参数</param>
        /// <param name="callback">回调</param>
        /// <param name="nextState">下一个状态</param>
        private void AsyncFunctionWithCallback(string arg, Action<object> callback)
        {
            HttpUtil.HttpDownloadFileAsyncWithCallback(_testFileDownloadUrl, new HttpUtilAsyncState() { State = 0 }, obj =>
            {
                if (obj is Exception)
                {
                    Exception ex = obj as Exception;
                    Log("错误：" + ex.Message);
                }
                else
                {
                    MemoryStream ms = obj as MemoryStream;
                    string result = "带回调的异步方法 arg=" + arg + " 下载文件的字节数组长度=" + ms.Length;
                    callback(result);
                }
            });
        }
        #endregion

        #region 用Task模拟的异步方法

        /*

        #region 异步方法
        /// <summary>
        /// 异步方法
        /// </summary>
        private Task<string> AsyncFunction(string arg)
        {
            return Task.Run<string>(() =>
            {
                Thread.Sleep(2000);
                return "异步方法 arg=" + arg;
            });
        }
        #endregion

        #region 带回调的异步方法
        /// <summary>
        /// 带回调的异步方法
        /// </summary>
        /// <param name="arg">传入参数</param>
        /// <param name="callback">回调</param>
        /// <param name="nextState">下一个状态</param>
        private void AsyncFunctionWithCallback(string arg, Action<object> callback)
        {
            Task.Run(() =>
            {
                if (arg == "1")
                {
                    Thread.Sleep(2000);
                }
                else
                {
                    Thread.Sleep(2000);
                }

                string result = "带回调的异步方法 arg=" + arg;
                callback(result);
            });
        }
        #endregion

        */

        #endregion

    }
}
